package cn.st4rlight.util.value;

import static cn.st4rlight.util.collection.StreamUtil.safeStream;
import static cn.st4rlight.util.constant.NumberConstant.NUMBER_10000;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.ToLongFunction;

import javax.annotation.Nullable;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import cn.st4rlight.util.collection.StreamUtil;
import cn.st4rlight.util.exception.ServiceException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * 数值判断、操作
 *
 * @author st4rlight <st4rlight@163.com>
 * Created on 2024-04-13
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class NumberUtil {

    /**
     * null、0、正数的判断
     */
    public static boolean isNullOrZero(Integer num) {
        return Objects.isNull(num) || num == 0;
    }

    public static boolean isNullOrZero(Long num) {
        return Objects.isNull(num) || num == 0L;
    }

    public static boolean isPositive(Integer num) {
        return Objects.nonNull(num) && num > 0;
    }

    public static boolean isPositive(Long num) {
        return Objects.nonNull(num) && num > 0L;
    }

    public static boolean isNegtive(Integer num) {
        return Objects.nonNull(num) && num < 0;
    }

    public static boolean isNegtive(Long num) {
        return Objects.nonNull(num) && num < 0L;
    }

    public static boolean notNegtive(Integer num) {
        return Objects.nonNull(num) && num >= 0;
    }

    public static boolean notNegtive(Long num) {
        return Objects.nonNull(num) && num >= 0L;
    }


    /**
     * 用于判断一个输入的字符串是否是有效的long类型数值（> 0）
     */
    public static boolean isPositive(String digitNumber) {
        if (StringUtils.isBlank(digitNumber)
                || !NumberUtils.isDigits(digitNumber)
                || !NumberUtils.isParsable(digitNumber)
                || !NumberUtil.isValidLong(digitNumber)) {
            return false;
        }
        return NumberUtil.isPositive(Long.parseLong(digitNumber));
    }

    public static boolean allPositive(Integer... numbers) {
        if (ArrayUtils.isEmpty(numbers)) {
            return true;
        }
        return Arrays.stream(numbers).allMatch(NumberUtil::isPositive);
    }

    public static boolean allPositive(Long... numbers) {
        if (ArrayUtils.isEmpty(numbers)) {
            return true;
        }
        return Arrays.stream(numbers).allMatch(NumberUtil::isPositive);
    }

    public static boolean allPositive(String... numbers) {
        if (ArrayUtils.isEmpty(numbers)) {
            return true;
        }
        return Arrays.stream(numbers).allMatch(NumberUtil::isPositive);
    }

    public static boolean anyNotPositive(Integer... nums) {
        return !allPositive(nums);
    }

    public static boolean anyNotPositive(Long... nums) {
        return !allPositive(nums);
    }

    public static boolean anyNotPositive(String... nums) {
        return !allPositive(nums);
    }

    /**
     * 用于判断一个输入的字符串是否是有效的long类型数值
     */
    public static boolean isValidLong(String digitNumber) {
        try {
            Long.parseLong(digitNumber);
            return true;
        } catch (NumberFormatException ex) {
            return false;
        }
    }

    /**
     * 处理负数，最小为0，兼容null
     */
    public static Integer minTo0(@Nullable Integer amountValue) {
        return Math.max(DefaultUtil.nullToDefault(amountValue), 0);
    }

    public static Long minTo0(@Nullable Long amountValue) {
        return Math.max(DefaultUtil.nullToDefault(amountValue), 0L);
    }

    public static int long2Int(Long num) {
        return Objects.isNull(num) ? 0 : num.intValue();
    }

    /**
     * 金额转换
     */
    public static BigDecimal li2Yuan(Long amount) {
        return BigDecimal.valueOf(amount).divide(BigDecimal.valueOf(1000L), 3, RoundingMode.HALF_UP);
    }

    public static BigDecimal li2YuanScale2(Long amount) {
        return BigDecimal.valueOf(amount).divide(BigDecimal.valueOf(1000L), 2, RoundingMode.HALF_UP);
    }

    public static Long yuan2Li(String yuanStr) {
        if (!isValidBigDecimal(yuanStr)) {
            throw ServiceException.of(String.format("输入的金额信息异常: %s", yuanStr));
        }
        return new BigDecimal(yuanStr).multiply(BigDecimal.valueOf(1000L)).longValue();
    }

    /**
     * bool值转0和1
     */
    public static boolean num2Bool(Integer number) {
        return Objects.nonNull(number) && number != 0;
    }

    /**
     * 数字0和1转bool值
     */
    public static int bool2Num(Boolean flag) {
        return BooleanUtils.isTrue(flag) ? 1 : 0;
    }

    /**
     * 是否是有效的BigDecimal字符串，防止BigDecimal DDos攻击
     * 1. 不为空
     * 2. 不能使用科学计数法
     * 3. 长度必须小于16
     * 4. 必须是数值可转换
     */
    public static boolean isValidBigDecimal(String amountStr) {
        return StringUtils.isNotBlank(amountStr)
                && !amountStr.toLowerCase().contains("e")
                && amountStr.length() <= 16
                && NumberUtils.isParsable(amountStr);
    }

    /**
     * 小数转万分比，如0.283476 万分比四舍五入后是 2835
     */
    public static Optional<Long> decimalToTTH(String decimalStr) {
        if (StringUtils.isBlank(decimalStr)) {
            return Optional.empty();
        }
        // 避免BigDecimal DDos攻击
        if (decimalStr.toLowerCase().contains("e")) {
            throw ServiceException.of("万分比转换异常，不允许使用科学计数法");
        }
        // 只取前16位
        decimalStr = StringUtils.substring(decimalStr, 0, 16);

        BigDecimal bigDecimal = new BigDecimal(decimalStr).setScale(4, RoundingMode.HALF_UP);
        return Optional.of(bigDecimal.multiply(BigDecimal.valueOf(NUMBER_10000)).longValue());
    }


    /**
     * 转换并加和
     * NOTE: 使用时确保不会超过数值有效范围
     */
    public static <T> int sum2Int(Collection<T> originCollection, Function<T, Integer> mapper) {
        return StreamUtil.safeStream(originCollection)
                .map(mapper)
                .map(DefaultUtil::nullToDefault)
                .mapToInt(Integer::intValue)
                .sum();
    }

    public static <T> long sum2Long(Collection<T> originCollection, Function<T, Long> mapper) {
        return StreamUtil.safeStream(originCollection)
                .map(mapper)
                .map(DefaultUtil::nullToDefault)
                .mapToLong(Long::longValue)
                .sum();
    }

    /**
     * 注意默认返回的是0
     */
    public static <T> long sum2Long(Collection<T> originCollection, ToLongFunction<T> toLongFunction) {
        return safeStream(originCollection).mapToLong(toLongFunction).sum();
    }

    /**
     * 格式化，最多保留scale位小数
     */
    public static String formatPreciseScale(String originNum, int newScale) {
        if (StringUtils.isBlank(originNum)) {
            return StringUtils.EMPTY;
        }
        if (!NumberUtil.isValidBigDecimal(originNum)) {
            return originNum;
        }
        return new BigDecimal(originNum).setScale(newScale, RoundingMode.HALF_UP)
                .stripTrailingZeros().toPlainString();
    }

    /**
     * 格式化，最多保留scale位小数
     */
    public static String formatPreciseScale(String originNum, int newScale, boolean withPercentSymbol) {
        String formatNum = formatPreciseScale(originNum, newScale);
        if (StringUtils.isEmpty(formatNum) || !NumberUtil.isValidBigDecimal(formatNum)) {
            return formatNum;
        }
        return withPercentSymbol ? (formatNum + "%") : formatNum;
    }
}
